#include <condition_variable>
#include <chrono>
#include <fstream>
#include <unistd.h>
#include <sstream>
#include <iomanip>
#include <thread>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>

#include "sysIf.h"
#include "DeviceManager.h"
#include "HidDeviceManager.h"
#include "UpgraderServer.h"
#include "ProtocolApi.h"
#include "ConfigApi.h"

using namespace Protocol;
using namespace ConfigApi;

extern "C" {
extern int INI_KeyGetString(const char *pPath, const char *pSec, const char *pKey, char *pVal);
}


// 获取指定网络接口的 IP 地址
std::string getIpAddress(const std::string& interface) {
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock == -1) {
        return "";
    }

    struct ifreq ifr;
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, interface.c_str(), IFNAMSIZ - 1);

    if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) {
        close(sock);
        return "";
    }

    close(sock);
    struct sockaddr_in* addr = reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_addr);
    return inet_ntoa(addr->sin_addr);
}

// 获取指定网络接口的 MAC 地址
std::string getMacAddress(const std::string& interface) {
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock == -1) {
        return "";
    }

    struct ifreq ifr;
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, interface.c_str(), IFNAMSIZ - 1);

    if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) {
        close(sock);
        return "";
    }

    close(sock);
    std::ostringstream macAddress;
    macAddress << std::hex << std::setfill('0');
    for (int i = 0; i < 6; ++i) {
        macAddress << std::setw(2) << static_cast<int>(reinterpret_cast<unsigned char*>(&ifr.ifr_hwaddr.sa_data)[i]);
        if (i < 5) {
            macAddress << ":";
        }
    }
    return macAddress.str();
}


static std::string readFileToString(const std::string& filePath) {
    std::ifstream file(filePath); // 打开文件
    if (!file.is_open()) {
        errorf("Failed to open the file: %s", filePath.c_str());
        return "";
    }

    std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
    file.close(); // 关闭文件

    return content;
}

static void getInfo(std::string& soft_version, std::string& serial_number){
    //  /mnt/mtd/Sys/hardwareTest.ini
    char snid[64] = {0};
    INI_KeyGetString("/mnt/mtd/Sys/hardwareTest.ini", "hardwareTest", "snid", snid);
    serial_number = snid;
    // /usr/cfg/version_cfg
    soft_version = readFileToString("/usr/cfg/version_cfg");
}


static bool getDevName(std::string& name){
    Json::Value kv;
    int result = 0;
	std::string config_path = "DevName";
    bool ret = IConfigInterface::GetInstance()->Get(config_path, kv, result);
	if(ret && kv.isMember("Name")){
        name = kv["Name"].asString();
		return true;
    }
    return false;
}

static bool getProductInfo(std::string& productName, std::string& productModel){
    Json::Value devTable;
    ConfigApi::IConfigInterface::GetInstance()->Get("PdInfo", devTable);
    if(devTable.isMember("DeviceType")){
        std::string devType = devTable["DeviceType"].asString();
        if(devType == "ROOMS"){
            productName = "NearHub Rooms";
            productModel = productName;
        }else if(devType == "AIR"){
            productName = "NearHub AIR";
            productModel = devType;
        }
        return true;
    }

    return false;
}

DeviceManager::Ptr DeviceManager::Instance(){
    static auto ptr = std::make_shared<DeviceManager>();
    return ptr;
}

DeviceManager::DeviceManager(){
    m_last_upgrading = false;
    RegistRPC();
}

DeviceManager::~DeviceManager(){

}

void DeviceManager::RegistRPC(){
    IProtocolInterface::GetInstance()->AuditoryworksRegistRequestRpc("GetAgentVersion", 
        [this](const Json::Value& req_params, Json::Value& res_result, std::string& errinfo){
            return GetAgentVersion(req_params, res_result, errinfo);
        });

    IProtocolInterface::GetInstance()->AuditoryworksRegistRequestRpc("StartAgentUpgrade", 
        [this](const Json::Value& req_params, Json::Value& res_result, std::string& errinfo){
            return StartAgentUpgrade(req_params, res_result, errinfo);
        });

    IProtocolInterface::GetInstance()->AuditoryworksRegistRequestRpc("GetDeviceList", 
        [this](const Json::Value& req_params, Json::Value& res_result, std::string& errinfo){
            return GetDeviceList(req_params, res_result, errinfo);
        });

    IProtocolInterface::GetInstance()->AuditoryworksRegistRequestRpc("StartDeviceUpgrade", 
        [this](const Json::Value& req_params, Json::Value& res_result, std::string& errinfo){
            return StartDeviceUpgrade(req_params, res_result, errinfo);
        });

    IProtocolInterface::GetInstance()->AuditoryworksRegistRequestRpc("StartDeviceReboot", 
        [this](const Json::Value& req_params, Json::Value& res_result, std::string& errinfo){
            return StartDeviceReboot(req_params, res_result, errinfo);
        });

    IProtocolInterface::GetInstance()->AuditoryworksRegistRequestRpc("StartAgentReboot", 
        [this](const Json::Value& req_params, Json::Value& res_result, std::string& errinfo){
            std::thread th([]{
                std::this_thread::sleep_for(std::chrono::seconds(1));
                SYS_Reboot();
            });
            th.detach();
            return true;
        });

    IProtocolInterface::GetInstance()->AuditoryworksRegistRequestRpc("StartAgentShutDown", 
        [this](const Json::Value& req_params, Json::Value& res_result, std::string& errinfo){
            //to do ShutDown
            return true;
        });

    IProtocolInterface::GetInstance()->AuditoryworksRegistRequestRpc("StartDeviceSystemReset", 
        [this](const Json::Value& req_params, Json::Value& res_result, std::string& errinfo){
            return StartDeviceSystemReset(req_params, res_result, errinfo);
        });
}


bool DeviceManager::GetAgentVersion(const Json::Value& req_params, Json::Value& res_result, std::string& errinfo){
    std::string devname = "NearHub Dev";
    getDevName(devname);
    res_result["productName"] = devname;
    res_result["deviceName"] = devname;
    // res_result["device_model"] = "";

    std::string soft_version, serial_number;
    getInfo(soft_version, serial_number);

    res_result["deviceVersion"] = soft_version;
    res_result["serialNumber"] = serial_number;


    res_result["network"]["ethernet"]["ip"] = getIpAddress("eth0");
    res_result["network"]["ethernet"]["mac"] = getMacAddress("eth0");

    res_result["network"]["wifi"]["ip"] = getIpAddress("wlan0");
    res_result["network"]["wifi"]["mac"] = getMacAddress("wlan0");
    
    return true;
}


bool DeviceManager::StartAgentUpgrade(const Json::Value& req_params, Json::Value& res_result, std::string& errinfo){
    std::lock_guard<std::recursive_mutex> guard(m_mux);

    //check to repeated upgrades, and only allow one device can be upgraded at a time
    if(m_last_upgrading){
        errinfo = "There are already devices being upgraded";
        return false;
    }

    m_last_upgrading = true;
    if(req_params.isMember("url")){
        auto url = req_params["url"].asString();
        auto upgrade_lam = [url, this](){
            bool upgrade_done = false;
            auto lam = [&upgrade_done, url](){
                UpgraderServer::instance()->CloudUpgrade(url);
                upgrade_done = true;
            };

            std::thread th(lam);
            th.detach();

            int progress = 0;
            std::string soft_version, serial_number;
            getInfo(soft_version, serial_number);
            while(!upgrade_done){
                int progress = UpgraderServer::instance()->CloudProgress();
                tracef(" !!!!  UpgradeProgress %d ",progress);
                if(progress < 0){
                    break;
                }

                UpdateUpgradeProgress(serial_number, progress);
                std::this_thread::sleep_for(std::chrono::seconds(1));
            };
            
            m_last_upgrading = false;
            if(progress >= 100) return true;
            return false;
        };
        std::thread th(upgrade_lam);
        th.detach();
        return true;
    }

    m_last_upgrading = false;
    errinfo = "not found url";
    return false;
}

bool DeviceManager::GetDeviceList(const Json::Value& req_params, Json::Value& res_result, std::string& errinfo){
    auto hidDevices = HidDeviceManager::Instance()->GetHidDeviceList();
    std::lock_guard<std::recursive_mutex> guard(m_mux);
    Json::Value deviceArray;

    std::string soft_version, serial_number;
    getInfo(soft_version, serial_number);
    Json::Value rooms;
    
    std::string productName, productModel;
    getProductInfo(productName, productModel);

    std::string devname = "NearHub AIR";
    getDevName(devname);

    rooms["productName"] = devname;//productName;
    rooms["softVersion"] = soft_version;
    rooms["serialNumber"] = serial_number;
    rooms["productModel"] = productModel;
    deviceArray.append(rooms);
    for(auto it : hidDevices){

        Json::Value device;
        if(it.m_sub_devices.size() != 0){
            device["deviceCount"] = 1 + it.m_sub_devices.size();
            device["serialNumber"] = it.m_serial_number;
            
            Json::Value firstDevice;
            firstDevice["productName"] = it.m_product_name;
            firstDevice["phyVersion"] = it.m_phy_version;
            firstDevice["softVersion"] = it.m_soft_version;
            firstDevice["serialNumber"] = it.m_serial_number;
            firstDevice["productModel"] = it.m_product_model;
            firstDevice["mainDevice"] = true;
            firstDevice["uniqueId"] = it.m_unique_id;
            
            device["deviceInfo"].append(firstDevice);
            
            for(auto sub_device : it.m_sub_devices){
                Json::Value subTable;
                subTable["productName"] =  sub_device.m_product_name;
                subTable["phyVersion"] = sub_device.m_phy_version;
                subTable["softVersion"] = sub_device.m_soft_version;
                subTable["serialNumber"] =  sub_device.m_serial_number;
                subTable["productModel"] = it.m_product_model;
                subTable["uniqueId"] =  sub_device.m_unique_id;
                device["deviceInfo"].append(subTable);
            #if 0
                tracef("m_product_name: %s , m_product_model: %s , m_phy_version: %s , m_soft_version: %s , m_serial_number: %s , m_unique_id : %s \n"
                , sub_device.m_product_name.c_str(), sub_device.m_product_model.c_str(), sub_device.m_phy_version.c_str(), sub_device.m_soft_version.c_str(), 
                sub_device.m_serial_number.c_str(), sub_device.m_unique_id.c_str());    
            #endif
            }
        }else{
            device["productName"] = it.m_product_name;
            device["phyVersion"] = it.m_phy_version;
            device["softVersion"] = it.m_soft_version;
            device["serialNumber"] = it.m_serial_number;
            device["productModel"] = it.m_product_model;
        }

        deviceArray.append(device);
    }
    
    res_result["devices"] = deviceArray;
    if(deviceArray.size() > 0){
        return true;
    }else{
        errinfo = "not found any hid device";
        return false;
    }

    return true;
}

bool DeviceManager::StartDeviceUpgrade(const Json::Value& req_params, Json::Value& res_result, std::string& errinfo){
    std::lock_guard<std::recursive_mutex> guard(m_mux);

    //check to repeated upgrades, and only allow one device can be upgraded at a time
    if(m_last_upgrading){
        errinfo = "There are already devices being upgraded";
        return false;
    }

    m_last_upgrading = true;
    if(req_params.isMember("url") && req_params.isMember("serialNumber")){
        auto url = req_params["url"].asString();
        auto serial_number = req_params["serialNumber"].asString();
        std::string soft_version, local_serial_number;
        getInfo(soft_version, local_serial_number);
        if(local_serial_number == serial_number)
        {
            auto upgrade_lam = [url, this](){
                bool upgrade_done = false;
                auto lam = [&upgrade_done, url](){
                    UpgraderServer::instance()->CloudUpgrade(url);
                    upgrade_done = true;
                };

                std::thread th(lam);
                th.detach();

                int progress = 0;
                std::string soft_version, serial_number;
                getInfo(soft_version, serial_number);
                while(!upgrade_done){
                    int progress = UpgraderServer::instance()->CloudProgress();
                    tracef(" !!!!  UpgradeProgress %d ",progress);
                    if(progress < 0){
                        break;
                    }

                    UpdateUpgradeProgress(serial_number, progress);
                    std::this_thread::sleep_for(std::chrono::seconds(1));
                };
                
                m_last_upgrading = false;
                if(progress >= 100) return true;
                return false;
            };
            std::thread th(upgrade_lam);
            th.detach();
            return true;
        }

        //Requires sufficient storage space, Currently, only rooms product are considered
        std::string upgrade_file = "/extern/upgrade.bin";
        auto upgrade_lam = [url, serial_number, upgrade_file, this](){
            //1 download upgrade file
            bool done_ret = false;
            auto resultCb = [&done_ret](const Fdt::SockException &ex){
                if (ex) {
                    WarnL << "network err:" << ex.getErrCode() << " " << ex.what();
                } else {
                    WarnL << "succeed"  << ex.what();
                }
                done_ret = true;
            };

            int down_progress = 0;
            IProtocolInterface::GetInstance()->HttpDownloadFile(url, upgrade_file, resultCb, down_progress);
            while(!done_ret){
                std::this_thread::sleep_for(std::chrono::seconds(1));
            }

            if(down_progress != 100){
                m_last_upgrading = false;
                UpdateUpgradeProgress(serial_number, -1);
                return false;
            }

            //2 start upgrade 
            HidDeviceManager::Instance()->Upgrade(serial_number, upgrade_file, 
                [this](const std::string serial_number, int progress){
                    UpdateUpgradeProgress(serial_number, progress);
                });
            
            m_last_upgrading = false;
            return false;
        };
        std::thread th(upgrade_lam);
        th.detach();
        return true;
    }

    m_last_upgrading = false;
    errinfo = "not found url or serialNumber";
    return false;
}

 bool DeviceManager::StartDeviceReboot(const Json::Value& req_params, Json::Value& res_result, std::string& errinfo){
    if(req_params.isMember("serialNumber")){
        if(!HidDeviceManager::Instance()->Reboot(req_params["serialNumber"].asString())){
            errinfo = "the device not exist";
            return false;
        }else{
            return true;
        }
    }

    errinfo = "not found  serialNumber";
    return false;
 }


void DeviceManager::EventUpdateDevice(std::string& product_name, std::string& product_model, std::string& phy_version, 
        std::string& soft_version, std::string& serial_number, const std::string& updateType){
    Json::Value notifyData;
    Json::Value deviceArray;
    Json::Value device;
    device["productName"] = product_name;
    device["phyVersion"] = phy_version;
    device["softVersion"] = soft_version;
    device["serialNumber"] = serial_number;
    device["productModel"] = product_model;
    deviceArray.append(device);
    notifyData["devices"] = deviceArray;
    notifyData["updateType"] = updateType;
    IProtocolInterface::GetInstance()->AuditoryworksBroadcastEvent("EventUpdateDevice", notifyData);
}

void DeviceManager::UpdateDeviceList(){
    auto hidDevices = HidDeviceManager::Instance()->GetHidDeviceList();
    std::lock_guard<std::recursive_mutex> guard(m_mux);
    Json::Value deviceArray;
    Json::Value notifyData;

    std::string soft_version, serial_number;
    getInfo(soft_version, serial_number);
    Json::Value rooms;

    std::string productName, productModel;
    getProductInfo(productName, productModel);
    
    rooms["productName"] = productName;
    rooms["softVersion"] = soft_version;
    rooms["serialNumber"] = serial_number;
    rooms["productModel"] = productModel;
    deviceArray.append(rooms);

    for(auto it : hidDevices){
        Json::Value device;
        device["productName"] = it.m_product_name;
        device["phyVersion"] = it.m_phy_version;
        device["softVersion"] = it.m_soft_version;
        device["serialNumber"] = it.m_serial_number;
        device["productModel"] = it.m_product_model;
        deviceArray.append(device);
    }
    notifyData["devices"] = deviceArray;
    IProtocolInterface::GetInstance()->AuditoryworksBroadcastEvent("EventUpdateDeviceList", notifyData);
}

void DeviceManager::UpdateUpgradeProgress(const std::string& serial_number, int progress){
    Json::Value notifyData;
    notifyData["progress"] = progress;
    notifyData["serialNumber"] = serial_number;
    IProtocolInterface::GetInstance()->AuditoryworksBroadcastEvent("UpgradeProgress", notifyData);
}


bool DeviceManager::StartDeviceSystemReset(const Json::Value& req_params, Json::Value& res_result, std::string& errinfo){
    if(req_params.isMember("serialNumber")){
        if(!HidDeviceManager::Instance()->Reset(req_params["serialNumber"].asString())){
            errinfo = "the device not exist";
            return false;
        }else{
            return true;
        }
    }

    errinfo = "not found  serialNumber";
    return false;
}